

class GameState(object):
    """An instance of this class will keep track of a game and obey its rules."""

    def __init__(self, player_list, nr_stones_per_player):
        self.__player_list = player_list

        #init all stones:
        self.__stone_list = []
        for i in range(len(self.__player_list)):
            self.__stone_list.append({"black":(nr_stones_per_player / 2), "white":(nr_stones_per_player / 2)})

        self.__pandoras_box = {"black":0, "white":len(self.__player_list)}

        if nr_stones_per_player < 2 or not (nr_stones_per_player % 2) == 0:
            raise Exception("Invalid amount of stones per player specified.")
        self.__nr_stones_per_player = nr_stones_per_player

        self.__listener_list = []
        self.__next_player = 0
        self.__is_waiting_for_finished_move = False
        self.__is_finished = False
        self.__final_score_list = []
        self.__next_turn = 1
        self.__last_swap = None


    def is_finished(self):
        return self.__is_finished


    def next_turn(self):
        """
        Executes the next turn of the game.
        """
        if len(self.__player_list) < 1:
            raise Exception("Insufficient amount of players specified.")

        if self.is_finished():
            raise Exception("The game already finished!")

        if self.__is_waiting_for_finished_move:
            raise Exception("Previous player did not finish its move!")

        self.__is_waiting_for_finished_move = True
        self.__player_list[self.__next_player].do_move(self, self.__next_player, self.__stone_list[self.__next_player])


    def is_waiting(self):
        return self.__is_waiting_for_finished_move


    def finish_move(self, move):
        """To be called by a player to finish its move."""
        if self.is_finished():
            raise Exception("The game already finished!")

        if not self.__is_waiting_for_finished_move:
            raise Exception("The gamestate is not waiting for a move to be finished!")
        self.__is_waiting_for_finished_move = False

        #the stone that goes in pandoras box:
        stone = move["stone"]
        if stone == "black":
            if self.__stone_list[self.__next_player]["black"] < 1:
                raise Exception("Invalid move specified; player has no black stones!")
            self.__stone_list[self.__next_player]["black"] -= 1
            self.__pandoras_box["black"] += 1
        elif stone == "white":
            if self.__stone_list[self.__next_player]["white"] < 1:
                raise Exception("Invalid move specified; player has no white stones!")
            self.__stone_list[self.__next_player]["white"] -= 1
            self.__pandoras_box["white"] += 1
        else:
            raise Exception("Unexpected state (BUG)!")

        #game finished?:
        if self.__stone_list[self.__next_player]["black"] == 0 \
                and self.__stone_list[self.__next_player]["white"] == 0:
            self.__is_finished = True
            self.__stone_list[self.__next_player]["black"] = self.__pandoras_box["black"]
            self.__stone_list[self.__next_player]["white"] = self.__pandoras_box["white"]
            self.__pandoras_box["black"] = 0
            self.__pandoras_box["white"] = 0
            #calculate final scores:
            for i in range(len(self.__player_list)):
                score = 0
                score -= self.__stone_list[i]["black"]
                score += self.__stone_list[i]["white"]
                self.__final_score_list.append(score)

        #(possible) swapping of hands with another player:
        if not move["swap"] == -1 and not self.is_finished():
            if move["swap"] == self.__next_player:
                raise Exception("A player cannot swap stones with itself!")
            stones_to_swap = self.__stone_list[self.__next_player]
            self.__stone_list[self.__next_player] = self.__stone_list[move["swap"]]
            self.__stone_list[move["swap"]] = stones_to_swap
            self.__last_swap = {}
            self.__last_swap["from"] = self.__next_player
            self.__last_swap["to"] = move["swap"]
        else:
            self.__last_swap = None

        #inform listeners:
        move["player"] = self.__next_player
        for listener in self.__listener_list:
            listener.event_move(move)

        #next player:
        self.__next_player += 1
        if self.__next_player == len(self.__player_list):
            self.__next_player = 0

        self.__next_turn += 1


    def get_next_turn(self):
        return self.__next_turn


    def get_final_score_list(self):
        """Returns the final score list after the game has finished."""
        if not self.is_finished():
            raise Exception("Game is not yet finished!")
        return self.__final_score_list


    def get_player_list(self):
        return self.__player_list


    def add_listener(self, listener):
        self.__listener_list.append(listener)


    def print_current_state(self):
        """Prints the current state of the game."""
        print "pandoras box, black: %d, white: %d" % (self.__pandoras_box["black"], self.__pandoras_box["white"])
        for i in range(len(self.__stone_list)):
            stones = self.__stone_list[i]
            print "player %d, black: %d, white: %d" % (i, stones["black"], stones["white"])


    def get_stone_list(self):
        return self.__stone_list


    def get_pandoras_box(self):
        return self.__pandoras_box


    def get_last_swap(self):
        return self.__last_swap


    def get_next_player(self):
        """
        Returns current player if game is waiting for that player for its move to finish.
        """
        return self.__next_player


    def get_nr_stones_per_player(self):
        return self.__nr_stones_per_player
